﻿using Little.Soldier;
using Little.Soldier.SWIFT;
using Little.Soldier.SWIFT.Definition;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace Little.Soldier
{
    internal class SwiftXML
    {
        private const string InvalidStructure = "Invalid XML file structure.";

        public static SwiftMessage GetSwiftMessage(string xml)
        {
            try
            {
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(xml);
            if (xmlDocument.ChildNodes.Count == 0)
                throw new XmlException("Invalid XML file structure.");
            XmlNode firstChild = xmlDocument.FirstChild;
            int num1 = firstChild.Name.IndexOf('_');
            int num2 = -1;
            if (num1 >= 0)
            {
                num1 = firstChild.Name.IndexOf('_', num1 + 1);
                if (num1 >= 0)
                num2 = firstChild.Name.IndexOf('_', num1 + 1);
            }
            if (num1 < 0 || num2 < 0)
                throw new XmlException("Invalid XML file structure. Unexpected root node name.");
            string messageType = firstChild.Name.Substring(num1 + 3, num2 - num1 - 3);
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("SWIFT_CATEGORY");
            stringBuilder.Append(messageType.Substring(0, 1) == "S" ? "0" : messageType.Substring(0, 1));
            stringBuilder.Append("_MT");
            stringBuilder.Append(messageType);
            if (firstChild.Name.ToUpper() != ((object) stringBuilder).ToString() + "_INTERCHANGE")
                throw new XmlException("Invalid XML file structure. Unexpected root node name.");
            SwiftMessage swiftMessage = new SwiftMessage();
            swiftMessage.Block2 = (SwiftBlock2) new SwiftBlock2Input(swiftMessage, messageType, string.Empty, string.Empty, string.Empty, string.Empty);
            SwiftMessage message = FinancialMessages.GetSwiftMessage(swiftMessage);
            message.Block2 = (SwiftBlock2) null;
            foreach (XmlNode blockNode in firstChild.ChildNodes)
            {
                SwiftBlockBase swiftBlock = SwiftXML.GetSwiftBlockBase(blockNode, message, ((object)stringBuilder).ToString());
                if (swiftBlock != null)
                message.AddBlock(swiftBlock);
            }
            return message;
            }
            catch (XmlException ex)
            {
            throw;
            }
            catch (Exception ex)
            {
            throw new ApplicationException("Error parsing XML message.", ex);
            }
        }

        private static SwiftBlockBase GetSwiftBlockBase(XmlNode blockNode, SwiftMessage message, string nameOfBlock4)
        {
            switch (blockNode.Name.ToUpper())
            {
            case "BASICHEADERBLOCK":
                    return (SwiftBlockBase)SwiftXML.GetSwiftBlock1(blockNode, message);
            case "APPLICATIONHEADERBLOCK_INPUT":
                    return (SwiftBlockBase)SwiftXML.GetSwiftBlock2Input(blockNode, message);
            case "APPLICATIONHEADERBLOCK_OUTPUT":
                return (SwiftBlockBase)SwiftXML.GetSwiftBlock2Output(blockNode, message);
            case "BLOCK":
                return (SwiftBlockBase)SwiftXML.GetSwiftTagListBlock(blockNode, message);
            default:
                if (!(blockNode.Name.ToUpper() == nameOfBlock4.ToUpper()))
                throw new XmlException("Invalid XML file structure. Unexpected block node: " + blockNode.Name);
                SwiftBlock4 swiftBlock4 = new SwiftBlock4(message);
                SwiftMessageDefinition messageDefinition = message.MsgDefinition();
                foreach (XmlNode elementNode in blockNode.ChildNodes)
                    swiftBlock4.AddTags(SwiftXML.GetSwiftTags(elementNode, messageDefinition.Elements, true));
                return (SwiftBlockBase) swiftBlock4;
            }
        }

        private static SwiftTag[] GetSwiftTags(XmlNode elementNode, List<SwiftDefinitionElement> validElements, bool mustBeFound)
        {
            List<SwiftTag> list = new List<SwiftTag>();
            bool flag = false;
            foreach (SwiftDefinitionElement definitionElement in validElements)
            {
            if (definitionElement is SwiftTagChoiceDefinition)
            {
                SwiftTag[] swiftTagArray = SwiftXML.GetSwiftTags(elementNode, definitionElement.Elements, false);
                if (swiftTagArray.Length > 0)
                {
                flag = true;
                list.AddRange((IEnumerable<SwiftTag>) swiftTagArray);
                }
            }
            else if (definitionElement.Name.ToUpper() == elementNode.Name.ToUpper())
            {
                if (definitionElement is SwiftTagDefinition)
                {
                flag = true;
                string tagname = ((SwiftTagDefinition) definitionElement).TagName;
                while ((int) tagname[0] == 58)
                    tagname = tagname.Remove(0, 1);
                int length = tagname.IndexOf(':');
                if (length >= 0)
                    tagname = tagname.Substring(0, length);
                list.Add(new SwiftTag(tagname, elementNode.InnerText));
                break;
                }
                else if (definitionElement is SwiftTagSequenceDefinition)
                {
                flag = true;
                IEnumerator enumerator = elementNode.ChildNodes.GetEnumerator();
                try
                {
                    while (enumerator.MoveNext())
                    {
                    XmlNode elementNode1 = (XmlNode) enumerator.Current;
                    list.AddRange((IEnumerable<SwiftTag>)SwiftXML.GetSwiftTags(elementNode1, definitionElement.Elements, true));
                    }
                    break;
                }
                finally
                {
                    IDisposable disposable = enumerator as IDisposable;
                    if (disposable != null)
                    disposable.Dispose();
                }
                }
                else
                break;
            }
            }
            if (mustBeFound && !flag)
            throw new XmlException("Invalid XML file structure. Unexpected node in Block 4: " + elementNode.Name);
            else
            return list.ToArray();
        }

        private static SwiftBlock1 GetSwiftBlock1(XmlNode blockNode, SwiftMessage message)
        {
            string applicationIdentifier = string.Empty;
            string serviceIdentifier = string.Empty;
            string lTAddress = string.Empty;
            string sessionNumber = string.Empty;
            string sequenceNumber = string.Empty;
            foreach (XmlNode xmlNode in blockNode.ChildNodes)
            {
            switch (xmlNode.Name.ToUpper())
            {
                case "APPLICATIONIDENTIFIER":
                applicationIdentifier = xmlNode.InnerText;
                continue;
                case "SERVICEIDENTIFIER":
                serviceIdentifier = xmlNode.InnerText;
                continue;
                case "LTADDRESS":
                lTAddress = xmlNode.InnerText;
                continue;
                case "SESSIONNUMBER":
                sessionNumber = xmlNode.InnerText;
                continue;
                case "SEQUENCENUMBER":
                sequenceNumber = xmlNode.InnerText;
                continue;
                default:
                throw new XmlException("Invalid XML file structure. Unexpected node in Block 1: " + xmlNode.Name);
            }
            }
            return new SwiftBlock1(message, applicationIdentifier, serviceIdentifier, lTAddress, sessionNumber, sequenceNumber);
        }

        private static SwiftBlock2Output GetSwiftBlock2Output(XmlNode blockNode, SwiftMessage message)
        {
            string messageType = string.Empty;
            string senderInputTime = string.Empty;
            string messageInputReference_InputDate = string.Empty;
            string messageInputReference_LTAddress = string.Empty;
            string messageInputReference_SessionNumber = string.Empty;
            string messageInputReference_SequenceNumber = string.Empty;
            string receiverOutputDate = string.Empty;
            string receiverOutputTime = string.Empty;
            string messagePriority = string.Empty;
            foreach (XmlNode xmlNode1 in blockNode.ChildNodes)
            {
            switch (xmlNode1.Name.ToUpper())
            {
                case "MESSAGETYPE":
                messageType = xmlNode1.InnerText;
                continue;
                case "INPUTTIME":
                senderInputTime = xmlNode1.InnerText;
                continue;
                case "MESSAGEINPUTREFERENCE":
                IEnumerator enumerator = xmlNode1.ChildNodes.GetEnumerator();
                try
                {
                    while (enumerator.MoveNext())
                    {
                    XmlNode xmlNode2 = (XmlNode) enumerator.Current;
                    switch (xmlNode2.Name.ToUpper())
                    {
                        case "INPUTDATE":
                        messageInputReference_InputDate = xmlNode2.InnerText;
                        continue;
                        case "LTADDRESS":
                        messageInputReference_LTAddress = xmlNode2.InnerText;
                        continue;
                        case "SESSIONNUMBER":
                        messageInputReference_SessionNumber = xmlNode2.InnerText;
                        continue;
                        case "SEQUENCENUMBER":
                        messageInputReference_SequenceNumber = xmlNode2.InnerText;
                        continue;
                        default:
                        throw new XmlException("Invalid XML file structure. Unexpected node in Block 2: " + xmlNode1.Name);
                    }
                    }
                    continue;
                }
                finally
                {
                    IDisposable disposable = enumerator as IDisposable;
                    if (disposable != null)
                    disposable.Dispose();
                }
                case "RECEIVEROUTPUTDATE":
                receiverOutputDate = xmlNode1.InnerText;
                continue;
                case "RECEIVEROUTPUTTIME":
                receiverOutputTime = xmlNode1.InnerText;
                continue;
                case "MESSAGEPRIORITY":
                messagePriority = xmlNode1.InnerText;
                continue;
                default:
                throw new XmlException("Invalid XML file structure. Unexpected node in Block 2: " + xmlNode1.Name);
            }
            }
            return new SwiftBlock2Output(message, messageType, senderInputTime, messageInputReference_InputDate, messageInputReference_LTAddress, messageInputReference_SessionNumber, messageInputReference_SequenceNumber, receiverOutputDate, receiverOutputTime, messagePriority);
        }

        private static SwiftBlock2Input GetSwiftBlock2Input(XmlNode blockNode, SwiftMessage message)
        {
            string messageType = string.Empty;
            string destinationAddress = string.Empty;
            string messagePriority = string.Empty;
            string deliveryMonitoring = string.Empty;
            string obsolescencePeriod = string.Empty;
            foreach (XmlNode xmlNode in blockNode.ChildNodes)
            {
            switch (xmlNode.Name.ToUpper())
            {
                case "MESSAGETYPE":
                messageType = xmlNode.InnerText;
                continue;
                case "DESTINATIONADDRESS":
                destinationAddress = xmlNode.InnerText;
                continue;
                case "MESSAGEPRIORITY":
                messagePriority = xmlNode.InnerText;
                continue;
                case "DELIVERYMONITORING":
                deliveryMonitoring = xmlNode.InnerText;
                continue;
                case "OBSOLESCENCEPERIOD":
                obsolescencePeriod = xmlNode.InnerText;
                continue;
                default:
                throw new XmlException("Invalid XML file structure. Unexpected node in Block 2: " + xmlNode.Name);
            }
            }
            return new SwiftBlock2Input(message, messageType, destinationAddress, messagePriority, deliveryMonitoring, obsolescencePeriod);
        }

        private static SwiftTagListBlock GetSwiftTagListBlock(XmlNode blockNode, SwiftMessage message)
        {
            string blockName = string.Empty;
            XmlAttribute xmlAttribute = (XmlAttribute) null;
            if (blockNode.Attributes["Name"] != null)
            xmlAttribute = blockNode.Attributes["Name"];
            else if (blockNode.Attributes["name"] != null)
            xmlAttribute = blockNode.Attributes["name"];
            else if (blockNode.Attributes["NAME"] != null)
            xmlAttribute = blockNode.Attributes["NAME"];
            if (xmlAttribute != null)
            blockName = xmlAttribute.Value;
            SwiftTagListBlock swiftTagListBlock;
            switch (blockName)
            {
            case "3":
                swiftTagListBlock = (SwiftTagListBlock) new SwiftBlock3(message);
                break;
            case "4":
                swiftTagListBlock = (SwiftTagListBlock) new SwiftBlock4(message);
                break;
            case "5":
                swiftTagListBlock = (SwiftTagListBlock) new SwiftBlock5(message);
                break;
            default:
                swiftTagListBlock = (SwiftTagListBlock) new SwiftBlockUser(message, blockName);
                break;
            }
            foreach (XmlNode xmlNode in blockNode.ChildNodes)
            {
            string tagname = xmlNode.Name;
            if (tagname.StartsWith("Tag_") || tagname.StartsWith("tag_") || tagname.StartsWith("TAG_"))
                tagname = tagname.Remove(0, 4);
            SwiftTag swiftTag = new SwiftTag(tagname, xmlNode.InnerText);
            swiftTagListBlock.AddTag(swiftTag);
            }
            return swiftTagListBlock;
        }
    }
}
